package com.cq.hd.member.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cq.hd.common.enums.biz.*;
import com.cq.hd.common.response.Throw;
import com.cq.hd.common.utils.GenerateUtil;
import com.cq.hd.common.utils.LocalDateUtil;
import com.cq.hd.common.utils.NoUtils;
import com.cq.hd.common.utils.StringUtils;
import com.cq.hd.member.api.dto.AutoWithdrawalRecordDto;
import com.cq.hd.member.api.vo.BusinessEnterApplyDetailVo;
import com.cq.hd.member.api.vo.MerchantDetailVo;
import com.cq.hd.member.api.vo.MerchantEnterApplyVo;
import com.cq.hd.member.mapper.TbAutoWithdrawalRecordMapper;
import com.cq.hd.member.mq.provider.WithdrawalMsgProvider;
import com.cq.hd.member.po.TbAutoWithdrawalRecordPo;
import com.cq.hd.member.po.TbMerchantAmtBillPo;
import com.cq.hd.member.po.TbWithdrawalBillPo;
import com.cq.hd.member.service.*;
import com.cq.hd.order.api.TbOrderSettleBillApi;
import com.cq.hd.order.api.vo.OrderSettleBillVo;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author much
 * @since 2024/04/13 11:04
 */
@Slf4j
@Service
public class TbAutoWithdrawalRecordServiceImpl extends ServiceImpl<TbAutoWithdrawalRecordMapper, TbAutoWithdrawalRecordPo>
        implements TbAutoWithdrawalRecordService {

    @Resource
    private TbOrderSettleBillApi orderSettleBillApi;
    @Resource
    private TbMerchantService merchantService;
    @Resource
    private TbMerchantEnterApplyService merchantEnterApplyService;
    @Resource
    private TbBusinessEnterApplyService businessEnterApplyService;
    @Resource
    private TbWithdrawalBillService withdrawalBillService;
    @Resource
    private TbMerchantAmtBillService merchantAmtBillService;
    @Resource
    private PlatformTransactionManager transactionManager;
    @Resource
    private WithdrawalMsgProvider withdrawalMsgProvider;

    @Override
    public void addRecords(List<AutoWithdrawalRecordDto> autoWithdrawalRecords) {
        if (CollectionUtils.isEmpty(autoWithdrawalRecords)) {
            return;
        }
        List<TbAutoWithdrawalRecordPo> records = autoWithdrawalRecords.stream()
                .filter(e -> getZeroIfNull(e.getWithdrawalAmt()).compareTo(BigDecimal.ZERO) > 0)
                .map(this::convertToPo)
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(records)) {
            log.info("[AutoWithdrawal] - save [{}] record", records.size());
            saveBatch(records);
        }
    }

    /**
     * 发起自动提现申请
     */
    @Override
    public void scanAutoWithdrawalAndApply() {
        List<TbAutoWithdrawalRecordPo> waitAutoWithdrawalRecords = baseMapper.selectWaitWithdrawalRecords();
        log.info("[Auto Withdrawal] - start, size = {}", waitAutoWithdrawalRecords.size());
        if (CollectionUtils.isEmpty(waitAutoWithdrawalRecords)) {
            return;
        }
        for (TbAutoWithdrawalRecordPo record : waitAutoWithdrawalRecords) {
            // 查询OrderSettleBill是否已经打款成功，如果失败则不进行转账
            OrderSettleBillVo orderSettleBill = orderSettleBillApi.getById(record.getBillId()).unpack();
            if (orderSettleBill == null
                    || !OrderSettleBillTradeStateEnum.SUCCESS.getValue().equals(orderSettleBill.getTradeState())) {
                continue;
            }
            Long withdrawalId = autoWithdrawalApply(record);
            // 发送mq消息做异步提现打款，调用汇付提现接口
            withdrawalMsgProvider.sendMsg(withdrawalId);
        }
    }

    /**
     * 统计商户某个状态下的提现金额
     *
     * @param merchantId 商户id
     * @param state @see com.cq.hd.common.enums.biz.AutoWithdrawalStateEnum
     * @return
     */
    @Override
    public BigDecimal sumWithdrawalAmountByState(Long merchantId, Integer state) {
        return baseMapper.sumWithdrawalAmount(merchantId, state);
    }

    /**
     * 发起自动提现生情
     * 1.扣除商户本金、增加商户资金明细
     * 2.生成商户提现记录
     * 3.更新自动提现单的状态、回写提现记录id
     *
     * @param record 待提现记录
     * @return 提现记录单id
     */
    private Long autoWithdrawalApply(TbAutoWithdrawalRecordPo record) {
        MerchantDetailVo merchantDetailVo = merchantService.getDetailById(record.getMerchantId());
        // 校验商户
        checkMerchantBeforeWithdrawal(record, merchantDetailVo);
        // 提现账户信息
        HfWithdrawalAccount hfWithdrawalAccount = getHfWithdrawalAccount(merchantDetailVo);

        TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionAttribute());
        Long withdrawalId = 0L;
        try {
            // 扣除商户本金账户资金
            int subMerchantAmtCount = merchantService.subMerchantAmt(merchantDetailVo.getId(), record.getWithdrawalAmt());
            if (subMerchantAmtCount == 0) {
                log.error("自动提现失败，商户本金余额不足：{}", JSON.toJSONString(record));
                Throw.isBusinessException("商户本金账户余额不足");
            }
            // 提现记录
            TbWithdrawalBillPo withdrawalBill = saveWithdrawalBill(record, merchantDetailVo, hfWithdrawalAccount);
            withdrawalId = withdrawalBill.getId();
            // 资金明细
            saveMerchantAmtBill(merchantDetailVo.getId(), withdrawalBill);
            // 回写自动提现单状态和提现记录id
            updateAutoWithdrawalRecordByWithdrawal(record.getId(), withdrawalId);

            transactionManager.commit(transaction);
        } catch (Exception e) {
            //回滚事务
            transactionManager.rollback(transaction);
            log.error("<申请商户本金提现>事务异常回滚", e);
//            Throw.isBusinessException(e.getMessage());
        }
        return withdrawalId;
    }

    /**
     * 回写自动提现记录的状态，记录提现申请id
     *
     * @param id 自动提现申请记录id
     * @param withdrawalId 提现申请id
     */
    private void updateAutoWithdrawalRecordByWithdrawal(Long id, Long withdrawalId) {
        int res = baseMapper.updateToAlready(id, withdrawalId);
        if (res == 0) {
            Throw.isBusinessException("申请提现失败，回写自动提现记录状态错误");
        }
    }

    private void saveMerchantAmtBill(Long merchantId, TbWithdrawalBillPo withdrawalBill) {
        TbMerchantAmtBillPo tbMerchantAmtBillPo = new TbMerchantAmtBillPo();
        tbMerchantAmtBillPo.setBillNo(NoUtils.getMerchantAmtBillNo());
        tbMerchantAmtBillPo.setMerchantId(merchantId);
        tbMerchantAmtBillPo.setBillType(BillTypeEnum.EXPEND.getValue());
        tbMerchantAmtBillPo.setTradeType(MemberAmtBillTradeTypeEnum.WITHDRAWAL_AMT.getValue());
        tbMerchantAmtBillPo.setRelationTradeNo(withdrawalBill.getApplyNo());
        tbMerchantAmtBillPo.setBeforeAmt(withdrawalBill.getBeforeAmt());
        tbMerchantAmtBillPo.setTradeAmt(withdrawalBill.getTradeAmt());
        tbMerchantAmtBillPo.setAfterAmt(withdrawalBill.getAfterAmt());
        tbMerchantAmtBillPo.setTradeTime(withdrawalBill.getApplyTime());
        tbMerchantAmtBillPo.setCreateTime(LocalDateTime.now());
        tbMerchantAmtBillPo.setUpdateTime(LocalDateTime.now());
        boolean save = merchantAmtBillService.save(tbMerchantAmtBillPo);
        if (!save) {
            Throw.isBusinessException("申请提现失败");
        }
    }

    private TbWithdrawalBillPo saveWithdrawalBill(
            TbAutoWithdrawalRecordPo record, MerchantDetailVo merchantDetailVo, HfWithdrawalAccount hfWithdrawalAccount) {
        LocalDateTime nowLocalDateTime = LocalDateUtil.getNowLocalDateTime();
        String yyyyMMdd = LocalDateUtil.toLocalDateToString(nowLocalDateTime.toLocalDate(), "yyyyMMdd");
        String reqSeqId = LocalDateUtil.toLocalDateTimeToString(nowLocalDateTime, "yyyyMMddHHmmss") + GenerateUtil.getRandomNum(5);

        TbWithdrawalBillPo tbWithdrawalBillPo = new TbWithdrawalBillPo();
        tbWithdrawalBillPo.setApplyNo(NoUtils.getWithdrawalBillNo());
        tbWithdrawalBillPo.setWithdrawalUserId(record.getMerchantId());
        tbWithdrawalBillPo.setBusinessId(0L);
        tbWithdrawalBillPo.setWithdrawalType(WithdrawalTypeEnum.MERCHANT_AUTO_AMT.getValue());
        tbWithdrawalBillPo.setState(WithdrawalStateEnum.AUDIT_SUCCESS.getValue());
        tbWithdrawalBillPo.setCardName(hfWithdrawalAccount.getCardName());
        tbWithdrawalBillPo.setCardNo(hfWithdrawalAccount.getCardNo());
        tbWithdrawalBillPo.setBankName(hfWithdrawalAccount.getBankName());
        tbWithdrawalBillPo.setBeforeAmt(merchantDetailVo.getBalanceAmt());
        tbWithdrawalBillPo.setTradeAmt(record.getWithdrawalAmt());
        BigDecimal afterAmt = merchantDetailVo.getBalanceAmt().subtract(record.getWithdrawalAmt());
        tbWithdrawalBillPo.setAfterAmt(afterAmt);
        tbWithdrawalBillPo.setFeeAmt(BigDecimal.ZERO);
        tbWithdrawalBillPo.setReceiveAmt(record.getWithdrawalAmt());
        tbWithdrawalBillPo.setApplyTime(nowLocalDateTime);
        tbWithdrawalBillPo.setReqDate(yyyyMMdd);
        tbWithdrawalBillPo.setReqSeqId(reqSeqId);
        tbWithdrawalBillPo.setHfId(hfWithdrawalAccount.getHfId());
        tbWithdrawalBillPo.setAcctId(hfWithdrawalAccount.getAcctId());
        tbWithdrawalBillPo.setTokenNo(hfWithdrawalAccount.getTokenNo());
        tbWithdrawalBillPo.setCreateTime(nowLocalDateTime);
        tbWithdrawalBillPo.setUpdateTime(nowLocalDateTime);
        boolean save = withdrawalBillService.save(tbWithdrawalBillPo);
        if (!save) {
            Throw.isBusinessException("申请提现失败");
        }
        return tbWithdrawalBillPo;
    }

    /**
     * 获取汇付提现账号
     *
     * @param merchantDetailVo 商户详情
     * @return 汇付提现账号
     */
    private HfWithdrawalAccount getHfWithdrawalAccount(MerchantDetailVo merchantDetailVo) {
        HfWithdrawalAccount hfWithdrawalAccount = new HfWithdrawalAccount();
        hfWithdrawalAccount.setHfId(merchantDetailVo.getHfMerchantNo());
        hfWithdrawalAccount.setTokenNo(merchantDetailVo.getWithdrawalCardNo());
        String openid = merchantDetailVo.getOpenid();
        MerchantEnterApplyVo enterInfoByOpenid = merchantEnterApplyService.getSuccessEnterInfoByOpenid(openid);
        if (enterInfoByOpenid == null) {
            // 表示自有商户申请提现，查找商家入驻数据
            BusinessEnterApplyDetailVo businessEnterApplyDetailVo = businessEnterApplyService.getSuccessEnterInfoByOpenid(openid);
            if (businessEnterApplyDetailVo == null) {
                log.error("申请商户本金提现失败，未找到商家/商户入驻的数据，merchantId：{}", merchantDetailVo.getId());
                Throw.isBusinessException("商户数据错误");
            }
            hfWithdrawalAccount.setHfId(businessEnterApplyDetailVo.getHfMerchantNo());
            hfWithdrawalAccount.setAcctId(businessEnterApplyDetailVo.getHfAcctId());
            hfWithdrawalAccount.setTokenNo(businessEnterApplyDetailVo.getWithdrawalCardNo());
            hfWithdrawalAccount.setCardName(businessEnterApplyDetailVo.getCardName());
            hfWithdrawalAccount.setCardNo(businessEnterApplyDetailVo.getCardNo());
        } else {
            String bankCode = enterInfoByOpenid.getBankCode();
            if (!StringUtils.isBlank(bankCode)) {
                HfBankIdEnum hfBankIdEnum = HfBankIdEnum.typeOfValue(bankCode);
                if (hfBankIdEnum != null) {
                    hfWithdrawalAccount.setBankName(hfBankIdEnum.getName());
                }
            }
            hfWithdrawalAccount.setCardName(enterInfoByOpenid.getCardName());
            hfWithdrawalAccount.setCardNo(enterInfoByOpenid.getCardNo());
        }

        if (StringUtils.isBlank(hfWithdrawalAccount.getCardName()) || StringUtils.isBlank(hfWithdrawalAccount.getCardNo())) {
            log.error("申请商户本金提现失败，未找到商户的银行卡相关数据，merchantId：{}", merchantDetailVo.getId());
            Throw.isBusinessException("商户数据错误");
        }

        return hfWithdrawalAccount;
    }


    private void checkMerchantBeforeWithdrawal(TbAutoWithdrawalRecordPo record, MerchantDetailVo merchantDetailVo) {
        if (merchantDetailVo == null) {
            Throw.isBusinessException("请先成为商户");
        }
        if (record.getWithdrawalAmt().compareTo(merchantDetailVo.getBalanceAmt()) > 0) {
            Throw.isBusinessException("商户本金余额不足");
        }
        String openid = merchantDetailVo.getOpenid();
        if (StringUtils.isBlank(openid)) {
            log.error("申请商户本金提现失败，未找到商户的openid，merchantId：{}", record.getMerchantId());
            Throw.isBusinessException("商户数据错误");
        }
    }

    private BigDecimal getZeroIfNull(BigDecimal amt) {
        return Optional.ofNullable(amt).orElse(BigDecimal.ZERO);
    }

    private TbAutoWithdrawalRecordPo convertToPo(AutoWithdrawalRecordDto autoWithdrawalRecordDto) {
        TbAutoWithdrawalRecordPo tbAutoWithdrawalRecordPo = BeanUtil.copyProperties(autoWithdrawalRecordDto, TbAutoWithdrawalRecordPo.class);
        if (tbAutoWithdrawalRecordPo.getCreateTime() == null) {
            tbAutoWithdrawalRecordPo.setCreateTime(LocalDateTime.now());
        }
        if (tbAutoWithdrawalRecordPo.getUpdateTime() == null) {
            tbAutoWithdrawalRecordPo.setUpdateTime(LocalDateTime.now());
        }
        return tbAutoWithdrawalRecordPo;
    }

    /**
     * 汇付提现基本信息
     */
    @Data
    private static class HfWithdrawalAccount {

        String hfId;
        String acctId = "";
        String tokenNo;
        String cardName = "";
        String cardNo = "";
        String bankName = "";

    }

}
